﻿using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Maple.Weixin.MP.Exceptions;
using Maple.Weixin.MP.Helpers;

namespace Maple.Weixin.MP.Entities.Messages
{
    public abstract class RequestMessageBase : MessageBase, IEquatable<RequestMessageBase>
    {
        public virtual RequestMessageType MsgType {
            get { return RequestMessageType.Text; }
        }

        public long MsgId { get; private set; }

        public bool Encrypt { get; private set; }

        public XDocument XDocument { get; private set; } 

        protected virtual void FillPropertiesByXml(XDocument document) {
            this.ToUserName = document.GetElementValue("ToUserName", true);
            this.FromUserName = document.GetElementValue("FromUserName", true);
            this.CreateTime = document.GetTimeStampValue("CreateTime");
            if (MsgType != RequestMessageType.Event)
                this.MsgId = long.Parse(document.GetElementValue("MsgId", true));
        }

        public abstract T Accept<T>(RequestMessageVisitor<T> visitor);

        public ResponseMessageNothing CreateNothingResponse() {
            var response = new ResponseMessageNothing();
            return response;
        }

        public ResponseMessageImage CreateImageResponse(string mediaId) {
            var response = new ResponseMessageImage(mediaId, Encrypt)
                {
                    ToUserName = FromUserName,
                    FromUserName = ToUserName,
                    CreateTime = DateTime.Now
                };
            return response;
        }

        public ResponseMessageMusic CreateMusicResponse(string thumbMediaId) {
            var response = new ResponseMessageMusic(thumbMediaId, Encrypt)
                {
                    ToUserName = FromUserName,
                    FromUserName = ToUserName,
                    CreateTime = DateTime.Now
                };
            return response;
        }

        public ResponseMessageNews CreateNewsResponse(Article article) {
            var response = new ResponseMessageNews(Encrypt)
                {
                    ToUserName = FromUserName,
                    FromUserName = ToUserName,
                    CreateTime = DateTime.Now
                };
            response.Add(article);
            return response;
        }

        public ResponseMessageNews CreateNewsResponse(IEnumerable<Article> articles) {
            var response = new ResponseMessageNews(Encrypt)
                {
                    ToUserName = FromUserName,
                    FromUserName = ToUserName,
                    CreateTime = DateTime.Now
                };
            response.AddRange(articles);
            return response;
        }

        public ResponseMessageText CreateTextResponse(string content) {
            var response = new ResponseMessageText(content, Encrypt)
                {
                    ToUserName = FromUserName,
                    FromUserName = ToUserName,
                    CreateTime = DateTime.Now
                };
            return response;
        }

        public ResponseMessageVideo CreateVideoResponse(string mediaId) {
            var response = new ResponseMessageVideo(mediaId, Encrypt)
                {
                    ToUserName = FromUserName,
                    FromUserName = ToUserName,
                    CreateTime = DateTime.Now
                };
            return response;
        }

        public ResponseMessageVoice CreateVoiceResponse(string mediaId) {
            var response = new ResponseMessageVoice(mediaId, Encrypt)
                {
                    ToUserName = FromUserName,
                    FromUserName = ToUserName,
                    CreateTime = DateTime.Now
                };
            return response;
        }

        public ResponseMessageDkf CreateDkfResponse() {
            var response = new ResponseMessageDkf(this);
            return response;
        }

        public ResponseMessageDkf CreateDkfResponse(string kfAccount) {
            var response = new ResponseMessageDkf(this, kfAccount);
            return response;
        }

        #region Implementation of IEquatable<RequestMessageBase>

        public bool Equals(RequestMessageBase other) {
            if (other == null)
                return false;

            return this.MsgId == other.MsgId;
        }

        public override bool Equals(Object obj) {
            if (obj == null)
                return false;

            var message = obj as RequestMessageBase;
            return message != null && Equals(message);
        }

        public override int GetHashCode() {
            return this.MsgId.GetHashCode();
        }

        public static bool operator ==(RequestMessageBase message1, RequestMessageBase message2) {
            if ((object) message1 == null || ((object) message2) == null)
                return Equals(message1, message2);

            return message1.Equals(message2);
        }

        public static bool operator !=(RequestMessageBase message1, RequestMessageBase message2) {
            if (message1 == null || message2 == null)
                return !Equals(message1, message2);

            return !(message1.Equals(message2));
        }

        #endregion

        public static RequestMessageBase Create(XDocument document, bool encrypt = false) {
            if (document == null)
                throw new ArgumentNullException("document");

            var msgType = document.GetElementValue("MsgType", true);
            RequestMessageBase requestMessage;
            var messageType = EnumExtensions.ConvertToEnum<RequestMessageType>(msgType);
            switch (messageType) {
                case RequestMessageType.Text:
                    requestMessage = new RequestMessageText();
                    break;
                case RequestMessageType.Image:
                    requestMessage = new RequestMessageImage();
                    break;
                case RequestMessageType.Link:
                    requestMessage = new RequestMessageLink();
                    break;
                case RequestMessageType.Location:
                    requestMessage = new RequestMessageLocation();
                    break;
                case RequestMessageType.Video:
                    requestMessage = new RequestMessageVideo();
                    break;
                case RequestMessageType.Voice:
                    requestMessage = new RequestMessageVoice();
                    break;
                case RequestMessageType.Event:
                    var eventName = document.GetElementValue("Event", true).ToUpper();
                    switch (eventName) {
                        case "LOCATION": //地理位置
                            requestMessage = new RequestMessageEventLocation();
                            break;
                        case "SUBSCRIBE": //订阅（关注）
                            requestMessage = new RequestMessageEventSubscribe();
                            break;
                        case "UNSUBSCRIBE": //取消订阅（关注）
                            requestMessage = new RequestMessageEventUnsubscribe();
                            break;
                        case "CLICK": //菜单点击
                            requestMessage = new RequestMessageEventClick();
                            break;
                        case "SCAN": //二维码扫描
                            requestMessage = new RequestMessageEventScan();
                            break;
                        case "VIEW": //URL跳转
                            requestMessage = new RequestMessageEventView();
                            break;
                        default: //其他意外类型（也可以选择抛出异常）
                            throw new BadXmlFormatException(document);
                    }
                    break;
                default:
                    throw new BadXmlFormatException(document);
            }
            requestMessage.XDocument = document;
            requestMessage.FillPropertiesByXml(document);
            requestMessage.Encrypt = encrypt;
            return requestMessage;
        }
    }
}
